package com.feiwo.nonego.rpc.proxy.api.object;

import com.feiwo.nonego.rpc.common.util.ClassUtils;
import com.feiwo.nonego.rpc.protocol.RpcProtocol;
import com.feiwo.nonego.rpc.protocol.header.RpcHeaderFactory;
import com.feiwo.nonego.rpc.protocol.request.RpcRequest;
import com.feiwo.nonego.rpc.proxy.api.async.IAsyncObjectProxy;
import com.feiwo.nonego.rpc.proxy.api.consumer.Consumer;
import com.feiwo.nonego.rpc.proxy.api.future.RpcFuture;
import com.feiwo.nonego.rpc.registry.api.RegistryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 *
 *
 * @author feiwo 2025-08-08
 */
public class ObjectProxy<T> implements InvocationHandler, IAsyncObjectProxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectProxy.class);

    /**
     * 调用超时时间, 默认15秒
     */
    private static final long DEFAULT_TIMEOUT = 15000;

    /**
     * 接口Class对象
     */
    private final Class<T> clazz;

    /**
     * 服务版本
     */
    private String serviceVersion;

    /**
     * 服务分组
     */
    private String serviceGroup;

    /**
     * 服务消费者
     */
    private Consumer consumer;

    /**
     * 序列化类型
     */
    private String serializationType;

    /**
     * 是否异步调用
     */
    private boolean async;

    /**
     * 是否单向调用
     */
    private boolean oneway;

    private long timeout = DEFAULT_TIMEOUT;

    private RegistryService registryService;


    public ObjectProxy(Class<T> clazz) {
        this.clazz = clazz;
    }

    public ObjectProxy(Class<T> clazz, String serviceVersion, String serviceGroup, Consumer consumer, String serializationType, boolean async, boolean oneway, long timeout, RegistryService registryService) {
        this.clazz = clazz;
        this.serviceVersion = serviceVersion;
        this.serviceGroup = serviceGroup;
        this.consumer = consumer;
        this.serializationType = serializationType;
        this.async = async;
        this.oneway = oneway;
        this.timeout = timeout;
        this.registryService = registryService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 添加调试日志
        LOGGER.debug("Invoking method: {} with args: {}", method.getName(),
                args != null ? Arrays.toString(args) : "null");

        // 处理 Object 方法（如 toString, equals, hashCode）
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        var requestRpcProtocol = new RpcProtocol<RpcRequest>();
        requestRpcProtocol.setHeader(RpcHeaderFactory.getRequestHeader(serializationType));

        var request = new RpcRequest();
        request.setVersion(this.serviceVersion);
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParameterTypes(method.getParameterTypes());
        request.setParameters(args);
        request.setGroup(this.serviceGroup);
        request.setAsync(this.async);
        request.setOneway(this.oneway);
        requestRpcProtocol.setBody(request);

        LOGGER.debug("服务消费者发送的数据 ---> {}", requestRpcProtocol);
        LOGGER.debug("object proxy class name ---> {}", method.getDeclaringClass().getName());
        LOGGER.debug("object proxy method name ---> {}", method.getName());

        for (Class<?> parameterType : method.getParameterTypes()) {
            LOGGER.debug("object proxy method parameter type ---> {}", parameterType.getName());
        }
        for (Object arg : args) {
            LOGGER.debug("object proxy method parameter value ---> {}", arg);
        }

        var future = this.consumer.sendRequest(requestRpcProtocol, this.registryService);
        return future.isPresent() ? timeout > 0 ? future.get().get(timeout, TimeUnit.MILLISECONDS) : future.get().get() : null;
    }

    @Override
    public Optional<RpcFuture> call(String methodName, Object... args) {
        var request = createRequest(clazz.getName(), methodName, args);
        try {
            return consumer.sendRequest(request, this.registryService);
        } catch (Exception e) {
            LOGGER.error("async call throws ex ---> ", e);
        }
        return Optional.empty();
    }

    private RpcProtocol<RpcRequest> createRequest(String className, String methodName, Object[] args) {
        var requestRpcProtocol = new RpcProtocol<RpcRequest>();
        requestRpcProtocol.setHeader(RpcHeaderFactory.getRequestHeader(serializationType));

        var request = new RpcRequest();
        request.setClassName(className);
        request.setMethodName(methodName);
        request.setParameters(args);
        request.setVersion(this.serviceVersion);
        request.setGroup(this.serviceGroup);

        final var parameterTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = ClassUtils.getClassType(args[i]);
        }
        request.setParameterTypes(parameterTypes);
        requestRpcProtocol.setBody(request);

        return requestRpcProtocol;
    }


}
